package model

import (
	"errors"
	"fmt"
	"net"
	"pop-server/app/config"
	"pop-server/app/utils"
	"regexp"
	"strconv"
	"strings"
)

//用于POP返回邮件信息
type MailInfo struct {
	ID      int //索引id
	EmailID int //服务器中邮件真实id
	Size    int
}

type Status struct {
	OpStatus string
}

//三种状态：认可（APPROVAL）、处理（HANDLE）、更新（UPDATE）
var APPROVAL = "APPROVAL"
var HANDLE = "HANDLE"
var UPDATE = "UPDATE"

//服务运行
func Run() {
	for true {
		stat, _ := GetServerStatus()
		if stat == "T" {
			POPListen()
		} else {
			continue
		}
	}

}

//获取服务开启状态
func GetServerStatus() (status string, err error) {
	row := Db.
		Table("server").
		Select("pop_on").
		Where("id = ?", 1).Row()
	err = row.Scan(&status)
	return
}

//监听110端口
func POPListen() {
	listen, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: 110, Zone: ""})
	if err != nil {
		config.GetLogger().Warnw("监听端口失败",
			"err: ", err,
		)
		return
	}
	config.GetLogger().Info("正在监听110端口")
	defer listen.Close()

	config.GetLogger().Info("准备接收来自110端口的数据")
	POPReceive(listen)

}

//接收110端口进来的数据
func POPReceive(listen *net.TCPListener) {

	for {
		//判断服务器服务启停状态
		stat, _ := GetServerStatus()
		if stat == "F" {
			return
		}

		//接收连接
		conn, err := listen.AcceptTCP()
		defer conn.Close()

		//客户端连接异常
		if err != nil {
			config.GetLogger().Warnw("客户端连接异常",
				"err: ", err.Error(),
			)
			continue
		}
		_, _ = conn.Write([]byte("+OK Mail Server POP3 ready" + CRLF))
		config.GetLogger().Info("新客户端", conn.RemoteAddr().String())

		go func() {
			//创建待处理字节切片
			tmpBuffer := make([]byte, 0)
			//创建channel通信管道
			readerChannel := make(chan []byte, 16)
			user := &Info{}
			user.Init()
			stat := &Status{}
			stat.OpStatus = APPROVAL
			//channel信息接收端
			go POPReader(readerChannel, conn, user, stat)
			config.GetLogger().Info("正在试图从连接中读取信息并进行响应")

			//创建输出缓冲切片
			data := make([]byte, 1024)

			for {
				//从Socket读取数据
				i, err := conn.Read(data)
				//读取失败发生错误
				if err != nil {
					config.GetLogger().Warnw("读取发生错误",
						"err: ", err.Error(),
					)
					return
				}
				//用自定义协议处理字节

				tmpBuffer = utils.DepackMail(append(tmpBuffer, data[:i]...), readerChannel)
				//fmt.Println(tmpBuffer, string(tmpBuffer))
				//fmt.Println(data,string(data))
			}

		}()

	}
}

//针对TCP连接传过来的数据报文进行响应
func POPReader(readerChannel chan []byte, conn *net.TCPConn, user *Info, status *Status) {

	for {
		select {
		case data := <-readerChannel:
			//fmt.Println(string(data))
			switch strings.ToUpper(string(data[0:4])) {

			case "USER":
				//指定邮箱的字符串，这对服务器至关重要
				//仅在USER和PASS命令失败后或在“确认”状态中使用
				//+OK：有效邮箱；
				//-ERR：无效邮箱

				matchResult, _ := regexp.MatchString(`USER \S+`, strings.ToUpper(string(data)))
				//参数错误
				if !matchResult {
					_, _ = conn.Write([]byte("-ERR Parameter Error" + CRLF))
					config.GetLogger().Info("-ERR Parameter Error")
					break
				}

				//判断状态
				if status.OpStatus != APPROVAL {
					_, _ = conn.Write([]byte("-ERR Status Error" + CRLF))
					config.GetLogger().Info("-ERR Status Error")
					break
				}

				//判断用户是否有效
				userEmail := strings.Split(string(data), SP)[1]
				effect, err := Verify(userEmail)
				if err != nil {
					config.GetLogger().Warnw("验证邮箱发生错误",
						"err: ", err.Error(),
					)
					_, _ = conn.Write([]byte("-ERR Verify User Failed" + CRLF))
					break
				}
				if !effect {
					config.GetLogger().Warnw("此用户无效: ", userEmail)
					_, _ = conn.Write([]byte("-ERR Sorry, No Mailbox For " + userEmail + " Here" + CRLF))
					break
				}
				user.Email = userEmail
				_, _ = conn.Write([]byte("+OK" + CRLF))
				config.GetLogger().Info("+OK")
				break
			case "PASS":
				//口令
				//仅在“确认”状态中USER命令成功后使用（明文传输的内容为MD5加密后的字符串）

				matchResult, _ := regexp.MatchString(`PASS \S+`, strings.ToUpper(string(data)))
				//参数错误
				if !matchResult {
					_, _ = conn.Write([]byte("-ERR Parameter Error" + CRLF))
					config.GetLogger().Info("-ERR Parameter Error")
					break
				}

				//判断状态
				if status.OpStatus != APPROVAL {
					_, _ = conn.Write([]byte("-ERR Status Error" + CRLF))
					config.GetLogger().Info("-ERR Status Error")
					break
				}

				//判断用户密码是否正确
				passwd := strings.Split(string(data), SP)[1]
				effect, err := VerifyPWD(user.Email, passwd)
				if err != nil {
					config.GetLogger().Info("验证邮箱发生错误")
					_, _ = conn.Write([]byte("-ERR Verify User Failed" + CRLF))
					break
				}
				if !effect {
					config.GetLogger().Info("此用户无效: ", user.User)
					_, _ = conn.Write([]byte("-ERR Authorization Failed" + CRLF))
					break
				}

				//更改状态
				status.OpStatus = HANDLE
				user.Password = passwd
				_, _ = conn.Write([]byte("+OK " + user.Email + " is Available" + CRLF))
				config.GetLogger().Info("+OK " + user.Email + " is Available")

				break
			case "STAT":
				//无参数，仅在“操作”状态下可用
				//“确认”响应由一个空格，以八进制表示的邮件数目，一个空格和邮件大小
				//被标记为删除的信件不在此列

				matchResult, _ := regexp.MatchString(`STAT`, strings.ToUpper(string(data)))
				//参数错误
				if !matchResult {
					_, _ = conn.Write([]byte("-ERR Parameter Error" + CRLF))
					config.GetLogger().Info("-ERR Parameter Error")
					break
				}

				//判断状态
				if status.OpStatus != HANDLE {
					_, _ = conn.Write([]byte("-ERR Status Error" + CRLF))
					config.GetLogger().Info("-ERR Status Error")
					break
				}

				//获取用户邮件信息
				sum, size, err := GetMailStatus(user)
				if err != nil {
					config.GetLogger().Warnw("获取用户邮件错误",
						"err: ", err.Error(),
					)
					_, _ = conn.Write([]byte("-ERR Get User Mail Failed" + CRLF))
					break
				}
				//响应连接
				_, _ = conn.Write([]byte("+OK " + strconv.Itoa(sum) + SP + strconv.Itoa(size) + CRLF))
				config.GetLogger().Info("+OK " + strconv.Itoa(sum) + SP + strconv.Itoa(size))

				break
			case "LIST":
				//信件数目（可选），如果出现，不包括标记为删除的信件
				//仅在“操作”状态下可用。
				matchResult1, _ := regexp.MatchString(`LIST \S+`, strings.ToUpper(string(data)))
				matchResult2, _ := regexp.MatchString(`LIST`, strings.ToUpper(string(data)))

				//参数错误
				if !matchResult1 && !matchResult2 {
					_, _ = conn.Write([]byte("-ERR Parameter Error" + CRLF))
					config.GetLogger().Info("-ERR Parameter Error")
					break
				}

				//判断状态
				if status.OpStatus != HANDLE {
					_, _ = conn.Write([]byte("-ERR Status Error" + CRLF))
					config.GetLogger().Info("-ERR Status Error")
					break
				}

				//根据是否携带参数选择对应的解析响应
				if matchResult1 {
					//携带参数
					arg, _ := strconv.Atoi(strings.Split(string(data), SP)[1])
					mailList, err := GetMailListArg(user, arg)
					if err != nil {
						if err.Error() == "数据索引错误" {
							config.GetLogger().Warnw("获取邮件错误",
								"err: ", err.Error(),
							)
							_, _ = conn.Write([]byte("-ERR No Such Message" + CRLF))
							break
						}

						config.GetLogger().Warnw("获取邮件错误",
							"err: ", err.Error(),
						)
						_, _ = conn.Write([]byte("-ERR Get User Mail Failed" + CRLF))
						break
					}

					//响应
					_, _ = conn.Write([]byte("+OK " + strconv.Itoa(mailList.ID) + SP + strconv.Itoa(mailList.Size) + CRLF))
					config.GetLogger().Info("+OK " + strconv.Itoa(mailList.ID) + SP + strconv.Itoa(mailList.Size))

				} else if matchResult2 {
					//未携带参数
					//获取对应用户的邮件列表
					sum, mailInfo, err := GetMailList(user)
					if err != nil {
						config.GetLogger().Warnw("获取邮件列表错误",
							"err: ", err.Error(),
						)
						_, _ = conn.Write([]byte("-ERR Get User Mail List Failed" + CRLF))
						break
					}

					//返回统计消息
					_, _ = conn.Write([]byte("+OK " + strconv.Itoa(sum) + " Messages" + CRLF))
					config.GetLogger().Info("+OK " + strconv.Itoa(sum) + " Messages")
					//返回邮件列表
					for i := 0; i < len(mailInfo); i++ {
						//fmt.Println([]byte(strconv.Itoa(mailInfo[i].ID) + SP + strconv.Itoa(mailInfo[i].Size)))
						_, _ = conn.Write([]byte(strconv.Itoa(mailInfo[i].ID) + SP + strconv.Itoa(mailInfo[i].Size) + CRLF))
						config.GetLogger().Info(strconv.Itoa(mailInfo[i].ID) + SP + strconv.Itoa(mailInfo[i].Size))
					}
					//终止符“."
					_, _ = conn.Write([]byte("." + CRLF))
					fmt.Println([]byte("." + CRLF))
				}
				break
			case "RETR":
				//不包括标记为删除的信件数目
				//如果服务器返回“确认”，给出的响应是多行的。在初始的+OK后，服务器发送与给定信息号对应的信息
				//仅在“操作”状态下可用

				matchResult, _ := regexp.MatchString(`RETR \d+`, strings.ToUpper(string(data)))
				//命令错误
				if !matchResult {
					_, _ = conn.Write([]byte("-ERR Parameter Error" + CRLF))
					config.GetLogger().Info("-ERR Parameter Error")
					break
				}

				//判断状态
				if status.OpStatus != HANDLE {
					_, _ = conn.Write([]byte("-ERR Status Error" + CRLF))
					config.GetLogger().Info("-ERR Status Error")
					break
				}

				//获取将要传输的邮件ID
				arg, _ := strconv.Atoi(strings.Split(string(data), SP)[1])
				mail, _ := GetMailListArg(user, arg)

				//获取邮件内容
				content, err := GetMail(mail.EmailID)
				if err != nil {
					config.GetLogger().Warnw("获取邮件内容错误",
						"err: ", err.Error(),
					)
					_, _ = conn.Write([]byte("-ERR Get User Mail Content Failed" + CRLF))
					break
				}

				//返回邮件内容，以"."结束
				_, _ = conn.Write([]byte("+OK" + CRLF + content + CRLF + "+OK Mission Accomplished"))
				config.GetLogger().Info("+OK" + CRLF + content + "+OK Mission Accomplished")
				//fmt.Println([]byte("+OK" + CRLF + content + CRLF))
				break
			case "DELE":
				//软删除邮件
				//服务器将此信件标记为删除，以后任何关于此信件的操作就会产生错误。服务器在会话进入“更新”状态前不会真正删除此信件。
				//仅在“操作”状态下可用。

				matchResult, _ := regexp.MatchString(`DELE \d+`, strings.ToUpper(string(data)))
				//命令错误
				if !matchResult {
					_, _ = conn.Write([]byte("-ERR Parameter Error" + CRLF))
					config.GetLogger().Info("-ERR Parameter Error")
					break
				}

				//判断状态
				if status.OpStatus != HANDLE {
					_, _ = conn.Write([]byte("-ERR Status Error" + CRLF))
					config.GetLogger().Info("-ERR Status Error")
					break
				}

				//获取将要删除的邮件ID
				arg, _ := strconv.Atoi(strings.Split(string(data), SP)[1])
				//fmt.Println(arg)
				mail, _ := GetMailListArg(user, arg)
				//fmt.Println(mail)
				//软删除邮件
				err := WeakDeleteMail(mail)
				if err != nil {
					if err.Error() == "邮件已被删除" {
						config.GetLogger().Warnw("删除邮件失败",
							"err: ", err.Error(),
						)
						_, _ = conn.Write([]byte("-ERR Mail Already Delete" + CRLF))
						break
					}
					config.GetLogger().Warnw("删除邮件失败",
						"err: ", err,
					)
					_, _ = conn.Write([]byte("-ERR Delete User Mail Failed" + CRLF))
					break
				}
				//返回邮件内容，以"."结束
				_, _ = conn.Write([]byte("+OK" + CRLF))
				config.GetLogger().Info("+OK")
				break
			case "NOOP":
				//服务器仅返回“确认”。
				//仅在“操作”状态下可用.

				matchResult, _ := regexp.MatchString(`NOOP`, strings.ToUpper(string(data)))
				//命令错误
				if !matchResult {
					_, _ = conn.Write([]byte("-ERR Parameter Error" + CRLF))
					config.GetLogger().Info("-ERR Parameter Error")
					break
				}

				//判断状态
				if status.OpStatus != HANDLE {
					_, _ = conn.Write([]byte("-ERR Status Error" + CRLF))
					config.GetLogger().Info("-ERR Status Error")
					break
				}
				break
			case "RSET":
				//所有被标记为删除的信件复位，服务器返回“确认”。
				//仅在“操作”状态下可用。

				matchResult, _ := regexp.MatchString(`RSET`, strings.ToUpper(string(data)))
				//命令错误
				if !matchResult {
					_, _ = conn.Write([]byte("-ERR Parameter Error" + CRLF))
					config.GetLogger().Info("-ERR Parameter Error")
					break
				}

				//判断状态
				if status.OpStatus != HANDLE {
					_, _ = conn.Write([]byte("-ERR Status Error" + CRLF))
					config.GetLogger().Info("-ERR Status Error")
					break
				}

				//删除了的邮件恢复
				err := ResetMail()
				if err != nil {
					config.GetLogger().Warnw("恢复邮件失败",
						"err: ", err,
					)
					_, _ = conn.Write([]byte("-ERR Reset User Mail Failed" + CRLF))
					break
				}
				//返回邮件内容，以"."结束
				_, _ = conn.Write([]byte("+OK" + CRLF))
				config.GetLogger().Info("+OK")
				break
			case "QUIT":
				matchResult, _ := regexp.MatchString(`QUIT`, strings.ToUpper(string(data)))
				//命令错误
				if !matchResult {
					_, _ = conn.Write([]byte("-ERR Parameter Error" + CRLF))
					config.GetLogger().Info("-ERR Parameter Error")
					break
				}

				//设置状态
				//当客户在“操作”状态下发送QUIT命令后，会话进入“更新”状态。
				//（注意：如果客户在“确认”状态下发送QUIT后，会话并不进入“更新”状态。）
				//如果会话因为QUIT命令以外的原因中断，会话并不进入“更新”状态，也不从服务器中删除任何信件。
				if status.OpStatus == HANDLE {
					status.OpStatus = UPDATE
				}
				if status.OpStatus == UPDATE {
					//进行删除邮件操作
					//pop要求删除邮件，而pop3则不要求在服务器上删除
				}

				//设置为初始状态
				status.OpStatus = APPROVAL

				//关闭连接
				_ = conn.Close()

				break
			default:
				_, _ = conn.Write([]byte("-ERR Order Error" + CRLF))
				config.GetLogger().Info("-ERR Order Error")
				break
			}

		}
	}
}

//获取用户邮箱中邮件数及所有字节数
func GetMailStatus(user *Info) (sum int, size int, err error) {
	err = GetUserMail(user)
	if err != nil {
		config.GetLogger().Warnw("数据获取错误",
			"err:", err,
		)
		return -1, -1, err
	}
	err = Db.
		Table("emails").
		Where("email_to = ? AND email_to_del = ?", user.Email, "F").
		Count(&sum).Error
	if err != nil {
		config.GetLogger().Warnw("数据获取错误",
			"err:", err,
		)
		return -1, -1, err
	}

	rows, err := Db.
		Table("emails").
		Where("email_to = ? AND email_to_del = ?", user.Email, "F").
		Select("email_size").Rows()
	if err != nil {
		config.GetLogger().Warnw("数据获取错误",
			"err:", err,
		)
		return -1, -1, err
	}

	for rows.Next() {
		tempSize := 0
		err = rows.Scan(&tempSize)
		if err != nil {
			config.GetLogger().Warnw("数据获取错误",
				"err:", err,
			)
			return -1, -1, err
		}
		size += tempSize
	}

	return sum, size, nil
}

//获取邮箱中邮件列表
func GetMailList(user *Info) (sum int, mailList []MailInfo, err error) {

	rows, err := Db.
		Table("emails").
		Where("email_to = ? AND email_to_del = ?", user.Email, "F").
		Select("id, email_size").
		Order("email_time").Rows()
	if err != nil {
		config.GetLogger().Warnw("数据获取错误",
			"err:", err,
		)
		return -1, mailList, err
	}

	id := 1
	for rows.Next() {
		tempMail := new(MailInfo)

		err = rows.Scan(&tempMail.EmailID, &tempMail.Size)
		if err != nil {
			config.GetLogger().Warnw("数据获取错误",
				"err:", err,
			)
			return -1, mailList, err
		}
		tempMail.ID = id

		mailList = append(mailList, *tempMail)

		id++
	}
	id--
	return id, mailList, err
}

//通过参数来查看具体一封邮件的大小
func GetMailListArg(user *Info, arg int) (mailList MailInfo, err error) {
	sum := 0

	err = Db.
		Table("emails").
		Where("email_to = ? AND email_to_del = ?", user.Email, "F").
		Count(&sum).Error
	if err != nil {
		config.GetLogger().Warnw("数据获取错误",
			"err:", err,
		)
		return mailList, err
	}

	if arg > sum {
		config.GetLogger().Warnw("数据索引错误",
			"err:", errors.New("数据索引错误"),
		)
		return mailList, errors.New("数据索引错误")
	}

	row := Db.
		Table("emails").
		Where("email_to = ? AND email_to_del = ?", user.Email, "F").
		Select("id, email_size").
		Order("email_time").
		Limit(1).
		Offset(arg - 1).Row()

	err = row.Scan(&mailList.EmailID, &mailList.Size)
	if err != nil {
		config.GetLogger().Warnw("数据获取错误",
			"err:", err,
		)
		return mailList, err
	}

	mailList.ID = arg

	return mailList, err
}

//返回一封邮件的所有内容
func GetMail(emailID int) (content string, err error) {
	mail := new(UserMails)
	err = Db.Table("emails").
		Select("user_email, email_from, email_to, email_subject, email_content, email_size, email_time").
		Where("id = ? AND email_to_del = ?", emailID, "F").Scan(&mail).Error
	if err != nil {
		config.GetLogger().Warnw("数据获取错误",
			"err:", err,
		)
		return "", err
	}
	content = "From: " + mail.EmailFrom + "\r\n" +
		"To: " + mail.EmailTo + "\r\n" +
		"Date: " + mail.EmailTime + "\r\n" +
		"Subject: " + mail.EmailSubject + "\r\n\r\n" +
		mail.EmailContent + "\r\n.\r\n"
	return content, nil
}

//邮件软删除
func WeakDeleteMail(mail MailInfo) (err error) {
	status := "F"
	email := new(UserMails)
	row := Db.Table("emails").
		Select("email_to_del").
		Where("id = ?", mail.EmailID).Row()

	err = row.Scan(&status)
	if err != nil {
		config.GetLogger().Warnw("邮件状态查询错误",
			"err:", err,
		)
		return
	}
	if status == "T" {
		return errors.New("邮件已被删除")
	}

	err = Db.Table("emails").Model(&email).
		Where("id = ?", mail.EmailID).
		Updates(map[string]interface{}{"email_to_del": "T"}).Error
	if err != nil {
		config.GetLogger().Warnw("邮件删除错误",
			"err:", err,
		)
		return
	}
	return
}

//恢复删除了的邮件
func ResetMail() (err error) {
	email := new(UserMails)
	err = Db.Table("emails").Model(&email).
		Where("email_to_del = ?", "T").
		Updates(map[string]interface{}{"email_to_del": "F"}).Error
	if err != nil {
		config.GetLogger().Warnw("邮件恢复错误",
			"err: ", err,
		)
		return err
	}
	return nil
}

//根据用户名获取用户ID
func GetUserID(username string) (ID string, err error) {
	err = Db.Table("users").Where("user_name = ?", username).Select("id").Scan(&ID).Error
	if err != nil {
		config.GetLogger().Warnw("数据获取错误",
			"err:", err,
		)
		return "", err
	}
	return ID, nil
}

//根据用户名获取用户邮箱
func GetUserMail(user *Info) (err error) {
	row := Db.Table("users").
		Where("user_email = ?", user.Email).
		Select("user_email").Row()
	err = row.Scan(&user.Email)
	if err != nil {
		config.GetLogger().Warnw("数据获取错误",
			"err:", err,
		)
		return
	}
	return
}

//根据用户名获取用户类型
func GetUserType(username string) (userType int, err error) {
	err = Db.Table("users").Where("user_name = ?", username).Select("user_type").Scan(&userType).Error
	if err != nil {
		config.GetLogger().Warnw("数据获取错误",
			"err:", err,
		)
		return -1, err
	}
	return userType, nil
}

//验证邮箱的有效性（仅支持项目邮箱）
func Verify(mailAddress string) (Effective bool, err error) {
	count := 0
	err = Db.Table("users").Where("user_email = ?", mailAddress).Count(&count).Error
	if err != nil {
		config.GetLogger().Warnw("数据获取错误",
			"err:", err,
		)
		return false, err
	}
	if count == 0 {
		return false, nil
	}
	return true, nil
}

//验证用户有效性
func VerifyUser(username string) (Effective bool, err error) {
	count := 0
	err = Db.Table("users").Where("user_name = ?", username).Count(&count).Error
	if err != nil {
		config.GetLogger().Warnw("数据获取错误",
			"err:", err,
		)
		return false, err
	}
	if count == 0 {
		return false, nil
	}
	return true, nil
}

//验证用户有效性
func VerifyPWD(userEmail string, password string) (Effective bool, err error) {
	count := 0
	err = Db.Table("users").Where("user_email = ? AND user_passwd = ?", userEmail, password).Count(&count).Error
	if err != nil {
		config.GetLogger().Warnw("数据获取错误",
			"err:", err,
		)
		return false, err
	}
	if count == 0 {
		return false, nil
	}
	return true, nil
}
